# -*- coding: utf-8 -*-
#(c)Jonathan & Gregoire (read __doc__ for more information)
#Copyright (C)%(author)s (%(date)s)
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.  If not, see <http://www.gnu.org/licenses/>.

## @package controller
# This package is used for control data given by the player.
# This structure is used for implement MVC pattern design

import  sys
import  random
import  re
from    engine.Singleton                import Singleton
from    engine.pattern.Talon            import Talon
from    engine.pattern.Grid             import Grid
from    engine.pattern.players.Human    import Human
from    engine.pattern.players.Computer import Computer
from    engine.view.Player_view         import Player_view
from    engine.view.Grid_view           import Grid_view
from    engine.pattern.cards.Card9      import Card9

__author__      = \
"""
Jonathan MERCIER <bioinfornatics at gmail.com >
Gregoire NAUDIN  <greg.naudin at gmail.com    >
"""
__version__     = "$Revision: 0.1 $"
__date__        = "$Date: 2009/23/09$"
__copyright__   = "Copyright (c) 2009 Jonathan MERCIER and Gregoire NAUDIN"
__license__     = "GPL v3 or under"
__name__        = "Game.py"
__description__ = \
"""
Game is for control the game event, such as:
- create player
- take a card from talon
- give a point to a player
etc ...
"""

## Documentation for Game class
# This class is the game definition object
# It is used for control the game event such as create player,take a card, give a point to a player etc ...
# @note: This class can be instanciated several times, but all game object have the same state.
# @see: Singleton
class Game(Singleton):
    ## The contructor
    # @param self: The pointer object
    def __init__(self):
        self.__player_list              = []
        self.__talon                    = Talon()
        self.__grid                     = Grid()
        ##  Add a start card in random position when object is created
        self.__grid.add_start_card()
    
    ## A generic answer for private attribute
    # @param self The object pointer
    def private(self):
        print u"this attribute is read only"
            
    ## Public setter for list_player
    # @param self The object pointer
    # @param value The newest value
    def set_player_list(self,value):
        self.__player_list = value
        
    ## Public getter for player_list
    # @param self The object pointer
    def get_player_list(self):
        return self.__player_list

    ## Property for player_list
    # @param self The object pointer
    player_list = property(get_player_list,set_player_list)
            
    ## Private method __create_human_player
    # @brief: It is a private method used for create a number of player object
    # @param self The object pointer
    # @param number_human_player The number of human player for this round
    def __create_human_player(self,number_human_player):
        verif_name  = []
        for i in range (number_human_player):
            name        = None
            team        = None
            player_view = Player_view()
            # Create computer player object
            human   = Human()
            # Set player name
            while name is None or name == "":
                name = player_view.set_player_name(human.get_type(),i+1)
                for player_name in verif_name:
                    if player_name == name:
                        player_view.name_already_use(name)
                        name = None
            verif_name.append(name)
            human.name = name
            # Set player team
            while team is None:
                team = player_view.set_player_team(human.get_type(),i+1)
            human.team = team
            self.__player_list.append(human)
         
    ## Private method __create_computer_player
    # @brief: It is a private method used for create a number of computer object
    # @param self The object pointer
    # @param number_computer_player The number of computer player for this round
    def __create_computer_player(self,number_computer_player):
        for i in range (number_computer_player):
            team = None
            player_view = Player_view()
            # Create human player object
            computer    = Computer()
            # Set player team
            while team is None:
                team = player_view.set_player_team(computer.get_type(),i+1)
            computer.team = team
            self.__player_list.append(computer)
            
    # Private method __create_player
    # @brief: It is a private method used for create a number of computer and human object.
    # @brief: After this send a message to his view : a update is needed
    # @param self The object pointer
    # @param number_human_player The number of human player for this round
    # @param number_computer_player The number of computer player for this round
    def __create_player(self,number_human_player,number_computer_player):
        if number_human_player is not None:
            self.__create_human_player(number_human_player)
        if number_computer_player is not None:
            self.__create_computer_player(number_computer_player)
            
    ## Private method __where_put_card
    # @param self The object pointer
    # @return x,y coordinate position in grid
    def __where_put_card(self):
        bad_coordinate      = True
        not_sure            = True
        x                   = None
        y                   = None
        bad_choice          = True
        choice              = None
        pattern1            = u'y|yes'
        pattern2            = u'n|no'
        regexp1             = re.compile(pattern1,re.IGNORECASE)
        regexp2             = re.compile(pattern2,re.IGNORECASE)
        answer1             = None
        answer2             = None
        grid_view           = Grid_view()
        
        while not_sure:
            while bad_coordinate:
                while x is None or x <0 or x >= self.__grid.get_x_length():
                    if x is not None:
                        grid_view.bad_x_value(x)
                    x = grid_view.where_put_card_on_x_axis()
                while y is None or y <0 or y >= self.__grid.get_y_length():
                    if y is not None:
                        grid_view.bad_y_value(y)
                    y = grid_view.where_put_card_on_y_axis()
                bad_coordinate = False
            while bad_choice:
                choice      = grid_view.are_you_sure(x,y)
                answer1     = regexp1.match(choice)
                answer2     = regexp2.match(choice)
                if answer1 is not None:
                    bad_choice  = False
                elif answer2 is not None:
                    not_sure    = False
                    bad_choice  = False
                else:
                    grid_view.choice_not_understand(choice)
                    bad_choice = True
            not_sure = False
        return x,y
    
    ## Private method __check_card_position
    # @param self The object pointer
    # @param player: The pointer of player object
    # @param card_number: The number of card
    # @param x: x axis
    # @param y: y axis
    # @return: bad_position a boolean True or False
    def __check_card_position(self,player,card_number,x,y):
        bad_position    = True
        error           = 0
        grid_view       = Grid_view()
        player_view     = Player_view()
        
        if self.__grid.get_card(x,y) is not None:
            error += 1
        else:
            if  x - 1 >= 0 and self.__grid.get_card(x-1,y) is not None:
                if player.get_card_border_up_left(card_number) == self.__grid.get_card_border_up_right(x-1,y):
                    error += 0
                else:
                    error += 1
                if player.get_card_border_left(card_number) == self.__grid.get_card_border_right(x-1,y):
                    error += 0
                else:
                    error += 1
                if player.get_card_border_down_left(card_number) == self.__grid.get_card_border_down_right(x-1,y):
                    error += 0
                else:
                    error += 1
            else:
                # Else is the grid border
                error += 0
            if x + 1 < self.__grid.get_x_length() and self.__grid.get_card(x+1,y) is not None:
                if  player.get_card_border_up_right(card_number)  == self.__grid.get_card_border_up_left(x+1,y):
                    error += 0
                else:
                    error += 1
                if  player.get_card_border_right(card_number)  == self.__grid.get_card_border_left(x+1,y):
                    error += 0
                else:
                    error += 1
                if  player.get_card_border_down_right(card_number)  == self.__grid.get_card_border_down_left(x+1,y):
                    error += 0
                else:
                    error += 1
            else:
                # Else is the grid border
                error += 0
            if  y + 1  < self.__grid.get_y_length() and self.__grid.get_card(x,y+1) is not None:
                if  player.get_card_border_down_left(card_number) == self.__grid.get_card_border_up_left(x,y+1):
                    error += 0
                else:
                    error += 1
                if  player.get_card_border_down(card_number) == self.__grid.get_card_border_up(x,y+1):
                    error += 0
                else:
                    error += 1
                if  player.get_card_border_down_right(card_number) == self.__grid.get_card_border_up_right(x,y+1):
                    error += 0
                else:
                    error += 1
            else:
                # Else is the grid border
                error += 0
            if  y - 1 >= 0 and self.__grid.get_card(x,y-1) is not None:
                if  player.get_card_border_up_left(card_number) == self.__grid.get_card_border_down_left(x,y-1):
                    error += 0
                else:
                    error += 1
                if  player.get_card_border_up(card_number) == self.__grid.get_card_border_down(x,y-1):
                    error += 0
                else:
                    error += 1
                if  player.get_card_border_up_right(card_number) == self.__grid.get_card_border_down_right(x,y-1):
                    error += 0
                else:
                    error += 1                
            else:
                # Else is the grid border
                error += 0
                
        if error == 0:
            bad_position = False
            card = player.drop_card(card_number)
            self.__grid.add_card(card,x,y)
            self.__compute_score(player, x, y)
        else:
            self.show_grid()
            bad_position    = grid_view.bad_position(x,y)
            card            = player.get_card(card_number)
            player_view.show_card(player.name,card)
            
        return bad_position
                
                    
    def show_grid(self):
        grid_view   = Grid_view()
        i           = 0
        j           = 0
        list2D      = self.__grid.get_grid()
        for raw in list2D:
            for card in raw:
                if card is None:
                    empty_card = Card9()
                    list2D[i][j] = empty_card
                j += 1
            j  = 0
            i += 1
        grid_view.show(list2D)
    
    
    def __computing(self,x,y):
        if x+1 == self.__grid.get_x_length():
            self.__check_and_compute_up_right(x,y)
            self.__check_and_compute_right(x,y)
            self.__check_and_compute_down_right(x,y)
        elif self.__grid.get_card( x+1, y) is not None:
            self.__check_and_compute_up_right(x,y)
            self.__check_and_compute_right(x,y)
            self.__check_and_compute_down_right(x,y)
            self.__check_and_compute_up_left(x+1,y)
            self.__check_and_compute_left(x+1,y)
            self.__check_and_compute_down_left(x+1,y)
            
        if x-1 <= 0:
            self.__check_and_compute_up_left(x,y)
            self.__check_and_compute_left(x,y)
            self.__check_and_compute_down_left(x,y)
        elif self.__grid.get_card( x-1, y) is not None:
            self.__check_and_compute_up_left(x,y)
            self.__check_and_compute_left(x,y)
            self.__check_and_compute_down_left(x,y)
            self.__check_and_compute_up_right(x-1,y)
            self.__check_and_compute_right(x-1,y)
            self.__check_and_compute_down_right(x-1,y)
            
        
        if y+1 == self.__grid.get_y_length():
            self.__check_and_compute_down_left(x,y)
            self.__check_and_compute_down(x,y)
            self.__check_and_compute_down_right(x,y)
        elif self.__grid.get_card( x, y+1) is not None:
            self.__check_and_compute_down_left(x,y)
            self.__check_and_compute_down(x,y)
            self.__check_and_compute_down_right(x,y)
            self.__check_and_compute_up_left(x,y+1)
            self.__check_and_compute_up(x,y+1)
            self.__check_and_compute_up_right(x,y+1)
                
        if y-1 <= 0:
            self.__check_and_compute_up_left(x,y)
            self.__check_and_compute_up(x,y)
            self.__check_and_compute_up_right(x,y)
        elif self.__grid.get_card( x, y-1) is not None:
            self.__check_and_compute_up_left(x,y)
            self.__check_and_compute_down(x,y)
            self.__check_and_compute_up_right(x,y)
            self.__check_and_compute_up_left(x,y-1)
            self.__check_and_compute_up(x,y-1)
            self.__check_and_compute_up_right(x,y-1)
            
    def __check_and_compute_up_left(self,x,y):
        if self.__grid.get_card_border_up_left(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_up_left(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_up_left(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
        
    def __check_and_compute_up(self,x,y):
        if self.__grid.get_card_border_up(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_up(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_up(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
        
    def __check_and_compute_up_right(self,x,y):
        if self.__grid.get_card_border_up_right(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_up_right(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_up_right(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
            
    def __check_and_compute_right(self,x,y):
        if self.__grid.get_card_border_right(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_right(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_up_right(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
            
    def __check_and_compute_down_right(self,x,y):
        if self.__grid.get_card_border_down_right(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_down_right(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_down_right(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
            
    def __check_and_compute_down(self,x,y):
        if self.__grid.get_card_border_down(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_down(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_down(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
            
    def __check_and_compute_down_left(self,x,y):
        if self.__grid.get_card_border_down_left(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_down_left(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_down_left(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)
            
    def __check_and_compute_left(self,x,y):
        if self.__grid.get_card_border_left(x,y) == u'R':
            value = self.__grid.get_card_nb_river(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_river(x, y, value)
        elif self.__grid.get_card_border_left(x,y) == u'P':
            value = self.__grid.get_card_nb_plain(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_plain(x, y, value)
        elif  self.__grid.get_card_border_left(x,y) == u'F':
            value = self.__grid.get_card_nb_forest(x, y)
            if value is not None:
                value  -= 1
                self.__grid.set_card_nb_forest(x, y, value)    
                   
    def __compute_score(self,player,x,y):
        
        self.__computing(x, y)
        if self.__grid.get_card_nb_river(x, y) is not None and self.__grid.get_card_nb_river(x, y) == 0:
            player.take_point(1)
            self.__grid.set_card_nb_river(x, y, None)
        elif self.__grid.get_card_nb_plain(x, y) is not None and self.__grid.get_card_nb_plain(x, y) == 0:
            player.take_point(2)
            self.__grid.set_card_nb_plain(x, y, None)
        elif self.__grid.get_card_nb_forest(x, y) is not None and self.__grid.get_card_nb_forest(x, y) == 0:
            player.take_point(5)
            self.__grid.set_card_nb_plain(x, y, None)
        else:
            pass # Do nothing no structure closed
            
    ## Public Method run
    # @brief: It is the game controller event
    # @param self The object pointer
    # @param number_human_player: The number of human player
    # @param number_computer_player: The number of computer player
    def run(self,number_human_player,number_computer_player):
        self.__create_player(number_human_player,number_computer_player)
        player_view         = Player_view()
        continue_game       = True
        ## If remain at least on card in the Talon
        while continue_game:
            ## Each player take one card and play
            for player in self.__player_list:
                if self.__talon.left_card() > 0 and not self.__grid.is_full():
                    to_continue         = True
                    self.show_grid()
                    
                    card = self.__talon.take_card()
                    player.take_card(card)
                    player_view.show_card(player.name,card)
                    player_view.current_score(player)
                    
                    if player.get_type() == u'Human':
                        while to_continue:
                            choice      = None
                            card_number = None
                            degree      = None
                            choice      = player_view.which_action(player.name)
                            
                            if choice == 1:
                                if player.card_remaining() == 1:
                                    card_number = 1
                                else:
                                    list_card=player.get_list_card()
                                    player_view.show_hand(list_card)
                                    while card_number is None or card_number < 1 or card_number > player.card_remaining():
                                        card_number  = player_view.ask_which_card_need_be_selected(player.card_remaining())
                                while degree is None :
                                    degree  = player_view.rotate_card()
                                    if not(degree is not 0 or abs(degree) is not 90 or abs(degree) is not 180 or abs(degree) is not 270 or abs(degree) is not 1):
                                        degree = None 
                                player.rotate_card(card_number,degree)
                                card = player.get_card(card_number)
                                self.show_grid()
                                player_view.show_card(player.name,card)
                                to_continue = True
                            elif choice == 2:
                                if player.card_remaining() == 1:
                                    card_number = 1
                                else:
                                    list_card=player.get_list_card()
                                    player_view.show_hand(list_card)
                                    while card_number is None or card_number < 1 or card_number > player.card_remaining():
                                        card_number  = player_view.ask_which_card_need_be_selected(player.card_remaining())
                                x,y     = self.__where_put_card()
                                to_continue = self.__check_card_position(player,card_number,x,y)
                            elif choice == 3:
                                print u"Player ",player.name," go turn"
                                to_continue = False
                    elif player.get_type() == u'Computer':
                        i=0
                        while to_continue:
                                x,y,card_number=player.ai(self.__grid.get_x_length()-1,self.__grid.get_y_length()-1)
                                to_continue = self.__check_card_position(player,card_number,x,y)
                                if not to_continue:
                                        i +=1
                                if 1>=3:
                                        to_continue=False
                else:
                    continue_game = False   
        for player in self.__player_list:
            if player.score > 1:
                print u"Player ",player.name,u": ",player.score,u" points"
            else:
                print u"Player ",player.name,u": ",player.score,u" points"
        print u"Push any key for exit",
        end = raw_input()
